package cn.yunbamboos.media.zlm.api;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.yunbamboos.media.zlm.client.ZLMClient;
import cn.yunbamboos.media.zlm.client.param.AddFFmpegSourceApiParams;
import cn.yunbamboos.media.zlm.client.param.AddStreamProxyApiParams;
import cn.yunbamboos.media.zlm.client.param.DelFFmpegSourceApiParams;
import cn.yunbamboos.media.zlm.client.param.DelStreamProxyApiParams;
import cn.yunbamboos.media.zlm.client.param.GenericParams;
import cn.yunbamboos.media.zlm.client.param.IParams;
import cn.yunbamboos.media.zlm.client.result.GenericResult;
import cn.yunbamboos.media.zlm.client.result.IResult;
import cn.yunbamboos.media.zlm.client.result.data.AddFFmpegSourceResultData;
import cn.yunbamboos.media.zlm.client.result.data.AddStreamProxyResultData;
import cn.yunbamboos.media.zlm.client.result.data.DelFFmpegSourceResultData;
import cn.yunbamboos.media.zlm.client.result.data.DelStreamProxyResultData;
import cn.yunbamboos.media.zlm.client.result.data.GenericResultData;
import cn.yunbamboos.media.zlm.client.result.data.IResultData;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ZLMApi {

    private static final Logger logger = LoggerFactory.getLogger(ZLMApi.class);

    private final ZLMClient client;

    public ZLMApi(ZLMClient client) {
        this.client = client;
    }

    /**
     * 0、/index/api/getApiList
     * 功能：获取API列表
     */
    public void getApiList() {
        IResult<GenericResultData> result = this.post("/index/api/getApiList");
        GenericResultData data = result.getData();
        logger.info("getApiList {} result: {}", result.isSuccess(), data.getData());
    }

    /**
     * 1、/index/api/getThreadsLoad
     * 功能：获取各epoll(或select)线程负载以及延时
     */
    public void getThreadsLoad() {
        IResult<GenericResultData> result = this.post("/index/api/getThreadsLoad");
        GenericResultData data = result.getData();
        logger.info("getThreadsLoad result: {}", data.getData());
    }

    /**
     * 2、/index/api/getWorkThreadsLoad
     * 功能：获取各后台epoll(或select)线程负载以及延时
     */
    public void getWorkThreadsLoad() {
        IResult<GenericResultData> result = this.post("/index/api/getWorkThreadsLoad");
        GenericResultData data = result.getData();
        logger.info("getWorkThreadsLoad result: {}", data.getData());
    }

    /**
     * 3、/index/api/getServerConfig
     * 功能：获取服务器配置
     */
    public void getServerConfig() {
        IResult<GenericResultData> result = this.post("/index/api/getServerConfig");
        GenericResultData data = result.getData();
        logger.info("getServerConfig result: {}", data.getData());
    }

    /**
     * 4、/index/api/setServerConfig
     * 功能：设置服务器配置
     */
    public void setServerConfig() {
        IResult<GenericResultData> result = this.post("/index/api/setServerConfig");
        GenericResultData data = result.getData();
        logger.info("setServerConfig result: {}", data.getData());
    }

    /**
     * 5、/index/api/restartServer
     * 功能：重启服务器,只有Daemon方式才能重启，否则是直接关闭！
     */
    public void restartServer() {
        IResult<GenericResultData> result = this.post("/index/api/restartServer");
        GenericResultData data = result.getData();
        logger.info("restartServer result: {}", data.getData());
    }

    /**
     * 6、/index/api/getMediaList
     * 功能：获取流列表，可选筛选参数
     * 参数	是否必选	释意
     *
     * @param schema N	筛选协议，例如 rtsp或rtmp
     * @param vhost  N	筛选虚拟主机，例如__defaultVhost__
     * @param app    N	筛选应用名，例如 live
     * @param stream N	筛选流id，例如 test
     */
    public void getMediaList(String schema, String vhost, String app, String stream) {
        IParams params = new GenericParams()
                .set("schema", schema, false)
                .set("vhost", vhost, false)
                .set("app", app, false)
                .set("stream", stream, false);
        IResult<GenericResultData> result = this.post("/index/api/getMediaList", params);
        GenericResultData data = result.getData();
        logger.info("getMediaList result: {}", data.getData());
    }

    /**
     * 7、/index/api/close_stream(已过期，请使用close_streams接口替换)
     * 功能：关闭流(目前所有类型的流都支持关闭)
     *
     * @param schema Y	协议，例如 rtsp或rtmp
     * @param vhost  Y	虚拟主机，例如__defaultVhost__
     * @param app    Y	应用名，例如 live
     * @param stream Y	流id，例如 test
     * @param force  N	是否强制关闭(有人在观看是否还关闭)
     */
    @Deprecated
    public void closeStream(String schema, String vhost, String app, String stream, boolean force) {
        IParams params = new GenericParams()
                .set("schema", schema, true)
                .set("vhost", vhost, true)
                .set("app", app, true)
                .set("stream", stream, true)
                .set("force", String.valueOf(force), false);
        IResult<GenericResultData> result = this.post("/index/api/close_stream", params);
        GenericResultData data = result.getData();
        logger.info("close_stream result: {}", data.getData());
    }

    /**
     * 8、/index/api/close_streams
     * 功能：关闭流(目前所有类型的流都支持关闭)
     *
     * @param schema N	协议，例如 rtsp或rtmp
     * @param vhost  N	虚拟主机，例如__defaultVhost__
     * @param app    N	应用名，例如 live
     * @param stream N	流id，例如 test
     * @param force  N	是否强制关闭(有人在观看是否还关闭)
     */
    public void closeStreams(String schema, String vhost, String app, String stream, boolean force) {
        IParams params = new GenericParams()
                .set("schema", schema, false)
                .set("vhost", vhost, false)
                .set("app", app, false)
                .set("stream", stream, false)
                .set("force", String.valueOf(force), false);
        IResult<GenericResultData> result = this.post("/index/api/close_streams", params);
        GenericResultData data = result.getData();
        logger.info("close_streams result: {}", data.getData());
    }

    /**
     * 9、/index/api/getAllSession
     * 功能：获取所有TcpSession列表(获取所有tcp客户端相关信息)
     */
    public void getAllSession() {

    }

    /**
     * 10、/index/api/kick_session
     * 功能：断开tcp连接，比如说可以断开rtsp、rtmp播放器等
     */
    public void kickSession() {

    }

    /**
     * 11、/index/api/kick_sessions
     * 功能：断开tcp连接，比如说可以断开rtsp、rtmp播放器等
     */
    public void kickSessions() {

    }

    /**
     * 12、/index/api/addStreamProxy
     * 功能：动态添加rtsp/rtmp/hls/http-ts/http-flv拉流代理(只支持H264/H265/aac/G711/opus负载)
     */
    public IResult<AddStreamProxyResultData> addStreamProxy(AddStreamProxyApiParams params) {
        return this.post("/index/api/addStreamProxy", params, new AddStreamProxyResultData(), AddStreamProxyResultData.class);
    }

    /**
     * 13、/index/api/delStreamProxy(流注册成功后，也可以使用close_streams接口替代)
     * 功能：关闭拉流代理
     */
    public IResult<DelStreamProxyResultData> delStreamProxy(DelStreamProxyApiParams params) {
        return this.post("/index/api/delStreamProxy", params, new DelStreamProxyResultData(), DelStreamProxyResultData.class);
    }

    /**
     * 14、/index/api/addFFmpegSource
     * 功能：通过fork FFmpeg进程的方式拉流代理，支持任意协议
     */
    public IResult<AddFFmpegSourceResultData> addFFmpegSource(AddFFmpegSourceApiParams params) {
        return this.post("/index/api/addFFmpegSource", params, new AddFFmpegSourceResultData(), AddFFmpegSourceResultData.class);
    }

    /**
     * 15、/index/api/delFFmpegSource(流注册成功后，也可以使用close_streams接口替代)
     * 功能：关闭ffmpeg拉流代理
     */
    public IResult<DelFFmpegSourceResultData> delFFmpegSource(DelFFmpegSourceApiParams params) {
        return this.post("/index/api/delFFmpegSource", params, new DelFFmpegSourceResultData(), DelFFmpegSourceResultData.class);
    }

    /**
     * 16、/index/api/isMediaOnline(已过期，请使用getMediaList接口替代)
     * 功能：判断直播流是否在线
     */
    @Deprecated
    public void isMediaOnline() {

    }

    /**
     * 17、/index/api/getMediaInfo(已过期，请使用getMediaList接口替代)
     * 功能：获取流相关信息
     */
    @Deprecated
    public void getMediaInfo() {

    }

    /**
     * 18、/index/api/getRtpInfo
     * 功能：获取rtp代理时的某路ssrc rtp信息
     */
    public void getRtpInfo() {

    }

    /**
     * 19、/index/api/getMp4RecordFile
     * 功能：搜索文件系统，获取流对应的录像文件列表或日期文件夹列表
     */
    public void getMp4RecordFile() {

    }

    /**
     * 20、/index/api/startRecord
     * 功能：开始录制hls或MP4
     */
    public void startRecord() {

    }

    /**
     * 21、/index/api/stopRecord
     * 功能：停止录制流
     */
    public void stopRecord() {

    }

    /**
     * 22、/index/api/isRecording
     * 功能：获取流录制状态
     */
    public void isRecording() {

    }

    /**
     * 23、/index/api/getSnap
     * 功能：获取截图或生成实时截图并返回
     */
    public void getSnap() {

    }

    /**
     * 24、/index/api/openRtpServer
     * 功能：创建GB28181 RTP接收端口，如果该端口接收数据超时，则会自动被回收(不用调用closeRtpServer接口)
     */
    public void openRtpServer() {

    }

    /**
     * 25、/index/api/closeRtpServer
     * 功能：关闭GB28181 RTP接收端口
     */
    public void closeRtpServer() {

    }

    /**
     * 26、/index/api/listRtpServer
     * 功能：获取openRtpServer接口创建的所有RTP服务器
     */
    public void listRtpServer() {

    }

    /**
     * 27、/index/api/startSendRtp
     * 功能：作为GB28181客户端，启动ps-rtp推流，支持rtp/udp方式；该接口支持rtsp/rtmp等协议转ps-rtp推流。第一次推流失败会直接返回错误，成功一次后，后续失败也将无限重试。
     */
    public void startSendRtp() {

    }

    /**
     * 27.1 、/index/api/startSendRtpPassive
     * 功能：作为GB28181 Passive TCP服务器；该接口支持rtsp/rtmp等协议转ps-rtp被动推流。调用该接口，zlm会启动tcp服务器等待连接请求，连接建立后，zlm会关闭tcp服务器，然后源源不断的往客户端推流。第一次推流失败会直接返回错误，成功一次后，后续失败也将无限重试(不停地建立tcp监听，超时后再关闭)。
     */
    public void startSendRtpPassive() {

    }

    /**
     * 28、/index/api/stopSendRtp
     * 功能：停止GB28181 ps-rtp推流
     */
    public void stopSendRtp() {

    }

    /**
     * 29、/index/api/getStatistic
     * 功能：获取主要对象个数统计，主要用于分析内存性能
     */
    public void getStatistic() {

    }

    /**
     * 30、/index/api/addStreamPusherProxy
     * 功能：添加rtsp/rtmp主动推流(把本服务器的直播流推送到其他服务器去)
     */
    public void addStreamPusherProxy() {

    }

    /**
     * 31、/index/api/delStreamPusherProxy(可以使用close_streams接口关闭源直播流也可以停止推流)
     * 功能：关闭推流
     */
    public void delStreamPusherProxy() {

    }

    /**
     * 32、/index/api/version(获取版本信息)
     * 功能：获取版本信息，如分支，commit id, 编译时间
     */
    public void version() {

    }

    /**
     * 33、/index/api/getMediaPlayerList
     * 功能：获取某个流观看者列表
     */
    public void getMediaPlayerList() {

    }

    /**
     * 34、VideoStack多视频流拼接宫格功能
     * 34.1 添加多屏拼接 (/index/api/stack/start)
     * 34.2 变更拼接参数 (/index/api/stack/reset)
     * 34.3 关闭多屏拼接 (/index/api/stack/stop)
     */

    private IResult<GenericResultData> post(String path) {
        return this.post(path, new GenericParams(), new GenericResultData(), GenericResultData.class);
    }

    private IResult<GenericResultData> post(String path, IParams params) {
        return this.post(path, params, new GenericResultData(), GenericResultData.class);
    }

    private <D extends IResultData> IResult<D> post(String path, IParams params, IResultData data, Class<D> clz) {
        String url = params.set("secret", client.getSecret(), true)
                .toUrl(StrUtil.format("{}://{}:{}{}", client.getSchema(), client.getIp(), client.getPort(), path));
        logger.debug("request url: {}", url);
        HttpRequest request = HttpRequest.post(url).timeout(10000);
        try (HttpResponse response = request.execute()) {
            int status = response.getStatus();
            logger.debug("request {} url: {}", status, url);
            if (response.isOk()) {
                String bodyStr = response.body();
                if (StrUtil.isNotBlank(bodyStr)) {
                    JSONObject body = JSON.parseObject(bodyStr);
                    int code = body.getIntValue("code");
                    if (code == 0) {
                        data.decode(body);
                        return new GenericResult<>(0, "", Convert.convert(clz, data));
                    } else {
                        String msg = body.getString("msg");
                        return new GenericResult<>(code, msg);
                    }
                }
                return new GenericResult<>(0, "");
            }
            String msg = response.body();
            logger.error("http 请求失败 status: {} msg: {}", status, msg);
            return new GenericResult<>(-1, StrUtil.format("http 请求失败 status: {} msg: {}", status, msg));
        }
    }
}
